
package app;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JLabel;

/**
 * Klasa określająca poruszanie się obiektów i odświeżanie obrazu dla użytkownika.
 * @author morohusky
 */
public class Play implements Runnable {
    private final Car[] _cars;
    private final AIController _ai = new AIController("car-miniAI");
    private final Controller _controller = new Controller();
    
    private boolean _isRunning = false;
    private boolean _isPaused = false;
    private Thread _thread;
    private double _fps = 0d;
    private long _lastFpsTime = 0;
    private long _lastRefresh;
    private final int TARGET_FPS = 60;
    private final long FRAME_TIME = 1000000000 / TARGET_FPS;
    private double _delta;
    
    private Timer _timerCars;
    
    
    
    
    //private Timer _drawingTimer;
    
    
    public Play() {
        //_controller.addControls(KeyEvent.VK_UP, KeyEvent.VK_DOWN, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT);
        //_controller.addControls(KeyEvent.VK_W, KeyEvent.VK_S, KeyEvent.VK_A, KeyEvent.VK_D);
        //_controller.addControls(KeyEvent.VK_NUMPAD8, KeyEvent.VK_NUMPAD5, KeyEvent.VK_NUMPAD4, KeyEvent.VK_NUMPAD6);
        //_controller.addControls(KeyEvent.VK_I, KeyEvent.VK_K, KeyEvent.VK_J, KeyEvent.VK_L);
        
        _cars = new Car[] { _ai.getCar() };//, new Car("car-mini", _controller.getNextControls()), };
            /*/new Car("car-mini2", _controller.getNextControls()) };/*//*
            , new Car("car-mini2", _controller.getNextControls())
            , new Car("car-mini", _controller.getNextControls()) };//*/
    }
    
    
    /**
     * Odświeża poruszające się obiekty
     * @param delta Opóźnienia w odświeżaniu
     */
    private synchronized void refreshGame(double delta) {
        _delta = delta;
        
        _timerCars = new Timer();
        _timerCars.schedule(new TimerTask() {
            @Override
            public synchronized void run() {
                if (!_isPaused) {
                    _ai.correct();
                }
                for (Car car : _cars) {
                    car.move(_delta);
                }
               _timerCars.cancel();
            }
        }, 0);
    }
    
    /**
     * Uruchamia poruszanie się obiektów
     * @param track Trasa, po której poruszać się będą obiekty
     * @param trackWidth Szerokość trasy
     */
    public void start(Track track, int trackWidth) {
        if (!_isPaused) {
            _ai.setTrack(track, trackWidth);
            
            /*      trzeba to przerobić, aby odświeżanie widoku odbywało się co 15 milisekund w tym timer'ze, a obliczenia powinny być w osobnym wątku z podobnym odświeżaniem
            _drawingTimer = new Timer();
            _drawingTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    redraw();
                }
            }, 0, 15);*/
            
            
            _isRunning = true;
            _thread = new Thread(this);
            _thread.start();
        }
        else
        {
            _isPaused = false;
        }
    }
    
    /**
     * Zakończenie poruszania się obiektów
     */
    public void stop() {
        _isPaused = false;
        _isRunning = false;
    }
    
    /**
     * Zatrzymanie poruszania się obiektów
     */
    public void pause() {
        _isPaused = true;
        _ai.reset();
    }
    
    /**
     * Pobranie ilości klatek na sekundę
     * @return 
     */
    public double getFps() {
        return _fps;
    }
    
    /**
     * Wyświetla lub ukrywa auta na trasie
     * @param visible Warunek wyświetlenia aut
     */
    public void setCarsVisible(boolean visible) {
        for (Car car : _cars) {
            car.getLabel().setVisible(visible);
        }
    }
    
    /**
     * "Getter" kontrolera
     * @return Kontroler
     */
    public Controller getController() {
        return _controller;
    }
    
    /**
     * "Getter" aut na trasie
     * @return Auta znajdujące się na trasie
     */
    public ArrayList<JLabel> getCars() {
        ArrayList<JLabel> result = new ArrayList();
        
        for (Car car : _cars) {
            result.add(car.getLabel());
        }
        
        return result;
    }
    
    /**
     * Wyśrodkowanie wszystkich aut na podanym obszarze
     * @param size Obszar, na którym wyśrodkowane będą auta
     */
    public void centerCarsOn(Dimension size) {
        for (Car car : _cars) {
            JLabel c = car.getLabel();
            car.setLocation((size.width - c.getWidth()) / 2, (size.height - c.getHeight()) / 2);
            car.reset();
        }
    }
    
    /**
     * Ustawienie wyszystkich aut w danej pozycji
     * @param x Położenie na osi x
     * @param y Położenie na osi y
     */
    public void setCarsPosition(int x, int y) {
        for (Car car : _cars) {
            JLabel c = car.getLabel();
            car.setLocation(x - (c.getWidth() / 2), y - (c.getHeight() / 2));
        }
    }
    
    @Override
    public synchronized void run() {
        _lastRefresh = System.nanoTime();
        while (_isRunning) {
            long now = System.nanoTime();
            long interval = now - _lastRefresh;
            _lastRefresh = now;
            
            double delta = interval / ((double)FRAME_TIME);
            
            _lastFpsTime += interval;
            _fps++;
            if (_lastFpsTime >= 1000000000)
            {
               //System.out.println("(FPS: " + _fps + ")");
               _lastFpsTime = 0;
               _fps = 0;
            }
            
            refreshGame(delta);
            
            try {
                long value = (long)((_lastRefresh - System.nanoTime() + FRAME_TIME) / 1000000);
                if (value > 0) {
                    wait(value);
                }
            }
            catch (InterruptedException e) { }
        }
    }
}
